From Nand to Tetris week 3
这次回顾第三章的内容,这部分主要介绍了时序门,寄存器,内存以及计数器的概念。
课程官网:
视频地址:
https://www.coursera.org/learn/build-a-computer
Chapter 3 时序逻辑
Part 1:课程回顾
之前两章介绍的芯片都是组合芯片(combinational logic),这些芯片的特点是和时间无关。这一讲引入时序芯片(sequential logic),之所以引入时序芯片,是为了构建能够记忆“信息”的芯片,而记忆必然是之前的,所以我们首先需要开发一些表示时间的芯片。
触发器(Flip-Flop)
计算机中最基本的时序单元是触发器(Flip-Flop),其实现的功能为
本课程中我们使用名为数据触发器(data flip-flops)的变体,其架构和输入输出如下:
DFF可以由与非门(NAND)实现,方法比较复杂,老师表示本课程略过这点,所以时序芯片的基本单元为DFF,其余常用的芯片都在Project中有详细介绍。
Part 2:项目
Bit
其架构如下
假设MUX的输出为muxout,DFF有两个输出,一个为out,另一个为dffout,有了这些变量之后,按照上述架构组合即可:
CHIP Bit {
IN in, load;
OUT out;
PARTS:
// Put your code here:
DFF (in=muxout, out=out, out=dffout);
Mux (a=dffout , b=in, sel=load, out=muxout);
}
参考资料:传送门
Register
接口如下
对每一位使用Bit芯片即可。
CHIP Register {
IN in[16], load;
OUT out[16];
PARTS:
// Put your code here:
Bit (in=in[0], load=load, out=out[0]);
Bit (in=in[1], load=load, out=out[1]);
Bit (in=in[2], load=load, out=out[2]);
Bit (in=in[3], load=load, out=out[3]);
Bit (in=in[4], load=load, out=out[4]);
Bit (in=in[5], load=load, out=out[5]);
Bit (in=in[6], load=load, out=out[6]);
Bit (in=in[7], load=load, out=out[7]);
Bit (in=in[8], load=load, out=out[8]);
Bit (in=in[9], load=load, out=out[9]);
Bit (in=in[10], load=load, out=out[10]);
Bit (in=in[11], load=load, out=out[11]);
Bit (in=in[12], load=load, out=out[12]);
Bit (in=in[13], load=load, out=out[13]);
Bit (in=in[14], load=load, out=out[14]);
Bit (in=in[15], load=load, out=out[15]);
}
RAM8
接口如下:
第一步利用DMux8Way对每个Register设置load值,只有相应地址的Register的load值设置为输入的load;第二步根据第一步产生的load值对每个Register操作;第三步利用Mux8Way16对第二步的结果进行选择:
CHIP RAM8 {
IN in[16], load, address[3];
OUT out[16];
PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address);
Register (in=in, load=a, out=o0);
Register (in=in, load=b, out=o1);
Register (in=in, load=c, out=o2);
Register (in=in, load=d, out=o3);
Register (in=in, load=e, out=o4);
Register (in=in, load=f, out=o5);
Register (in=in, load=g, out=o6);
Register (in=in, load=h, out=o7);
Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address ,out=out);
}
RAM64
将address分为两部分,第一部分查看属于哪个RAM8,第二部分查看属于哪个Register:
CHIP RAM64 {
IN in[16], load, address[6];
OUT out[16];
PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[3..5]);
RAM8 (in=in, load=a, address=address[0..2], out=o0);
RAM8 (in=in, load=b, address=address[0..2], out=o1);
RAM8 (in=in, load=c, address=address[0..2], out=o2);
RAM8 (in=in, load=d, address=address[0..2], out=o3);
RAM8 (in=in, load=e, address=address[0..2], out=o4);
RAM8 (in=in, load=f, address=address[0..2], out=o5);
RAM8 (in=in, load=g, address=address[0..2], out=o6);
RAM8 (in=in, load=h, address=address[0..2], out=o7);
Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[3..5], out=out);
}
RAM512,RAM4K,RAM16K和RAM64的思路类似。
RAM512
CHIP RAM512 {
IN in[16], load, address[9];
OUT out[16];
PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[6..8]);
RAM64 (in=in, load=a, address=address[0..5], out=o0);
RAM64 (in=in, load=b, address=address[0..5], out=o1);
RAM64 (in=in, load=c, address=address[0..5], out=o2);
RAM64 (in=in, load=d, address=address[0..5], out=o3);
RAM64 (in=in, load=e, address=address[0..5], out=o4);
RAM64 (in=in, load=f, address=address[0..5], out=o5);
RAM64 (in=in, load=g, address=address[0..5], out=o6);
RAM64 (in=in, load=h, address=address[0..5], out=o7);
Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[6..8], out=out);
}
RAM4K
CHIP RAM4K {
IN in[16], load, address[12];
OUT out[16];
PARTS:
// Put your code here:
DMux8Way (in=load, a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, sel=address[9..11]);
RAM512 (in=in, load=a, address=address[0..8], out=o0);
RAM512 (in=in, load=b, address=address[0..8], out=o1);
RAM512 (in=in, load=c, address=address[0..8], out=o2);
RAM512 (in=in, load=d, address=address[0..8], out=o3);
RAM512 (in=in, load=e, address=address[0..8], out=o4);
RAM512 (in=in, load=f, address=address[0..8], out=o5);
RAM512 (in=in, load=g, address=address[0..8], out=o6);
RAM512 (in=in, load=h, address=address[0..8], out=o7);
Mux8Way16(a=o0 ,b=o1 ,c=o2 ,d=o3 ,e=o4 ,f=o5 ,g=o6 ,h=o7, sel=address[9..11], out=out);
}
RAM16K
注意这里使用DMux4Way:
CHIP RAM16K {
IN in[16], load, address[14];
OUT out[16];
PARTS:
// Put your code here:
DMux4Way (in=load, a=a, b=b, c=c, d=d, sel=address[12..13]);
RAM4K (in=in, load=a, address=address[0..11], out=o0);
RAM4K (in=in, load=b, address=address[0..11], out=o1);
RAM4K (in=in, load=c, address=address[0..11], out=o2);
RAM4K (in=in, load=d, address=address[0..11], out=o3);
Mux4Way16(a=o0 ,b=o1 ,c=o2 ,d=o3, sel=address[12..13], out=out);
}
PC
接口如下:
这部分比较难,参考了课程讨论区:传送门
社区助教给出如下架构的提示:
Register存储上一次的值,只有当reset[t],load[t],inc[t]同时为false时,输出才为out[t],所以输入Register的load为
Register的输入为o2,输出有两个,一个为out,一个为Inc16的输入;Inc16的输出为Incout。Incout和in传入Mux16,根据load做选择,得到结果o1,最后将o1和false传入Mux16,根据reset做选择,得到输出o2。完整代码如下:
CHIP PC {
IN in[16],load,inc,reset;
OUT out[16];
PARTS:
// Put your code here:
//计算Register的load
Or (a=reset, b=load, out=r1);
Or (a=r1, b=inc, out=Regload);
//根据load选择
Mux16 (a=Incout, b=in, sel=load, out=o1);
//根据reset选择
Mux16 (a=o1, b=false, sel=reset, out=o2);
//根据Regload选择
Register (in=o2, load=Regload, out=out, out=o3);
//加一
Inc16 (in=o3, out=Incout);
}